<?php
/* --------------------------------------------------------------
   Captcha.inc.php 2023-06-02
   Gambio GmbH
   http://www.gambio.de
   Copyright (c) 2023 Gambio GmbH
   Released under the GNU General Public License (Version 2)
   [http://www.gnu.org/licenses/gpl-2.0.html]
   --------------------------------------------------------------
*/

/**
 * Class Captcha
 */
class Captcha
{
	protected $captchaType = '';
	protected $publicKey = '';
	protected $privateKey = '';
	protected $vvCode = '';
	protected $vvCodeName = '';
	protected $captchaTheme = '';
	protected $error = '';
	protected $resultHtml = '';

	protected static $RECAPTCHA_V2_VERIFICATION_SERVER = 'https://www.google.com/recaptcha/api/siteverify';


	// ######### CONSTRUCTOR #########


	/**
	 * @param string $vvCodeName
	 * @param string $captchaTheme
	 * @param int    $vvCodeLength
	 */
	public function __construct($vvCodeName = 'vvcode', $captchaTheme = 'white', $vvCodeLength = 6)
	{
		$this->publicKey = gm_get_conf('GM_RECAPTCHA_PUBLIC_KEY');
		$this->privateKey = gm_get_conf('GM_RECAPTCHA_PRIVATE_KEY');
		$this->vvCodeName = $vvCodeName;
		$this->captchaTheme = $captchaTheme;

		$this->captchaType = gm_get_conf('GM_CAPTCHA_TYPE');
		if($this->captchaType === 'standard')
        {
	        $this->captchaType = 'vvCode';
            $this->reload_vv_code($vvCodeLength);
        }
	}


	// ######### PUBLIC METHODS #########


	/**
	 * @param int $vvCodeLength
	 */
	public function reload_vv_code($vvCodeLength = 6)
	{
		include_once(DIR_FS_INC . 'xtc_random_charcode.inc.php');
		$this->vvCode = $_SESSION['vvcode'] ?? null;
		$vvCode = xtc_random_charcode($vvCodeLength);
		$_SESSION['vvcode'] = $vvCode;
	}

	/**
	 * @param array  $requestData
	 * @param string $section
	 * @param bool   $isAjaxRequest
	 *
	 * @return bool
	 */
	public function is_valid($requestData, $section = '', $isAjaxRequest = false)
	{
		$sectionIsSecured = gm_get_conf($section);

		if($sectionIsSecured === 'false')
		{
			return true;
		}

		switch ($this->captchaType)
		{
            case 'recaptcha_v2':

                return $this->_validateCaptchaTypeRecaptchaV2($requestData);

			case 'vvCode':

				return $this->_validateCaptchaTypeVvcode($requestData, $isAjaxRequest);

			default:
				return false;
		}
	}


	/**
	 * @return string
	 */
	public function get_html()
	{
		$this->prepare_data();
		return $this->resultHtml;
	}


	// ######### PROTECTED METHODS #########


	/**
	 * @return string
	 */
	protected function prepare_data()
	{
		switch ($this->captchaType)
		{
            case 'recaptcha_v2':
                $this->_getResultHtmlRecaptchaV2();
                break;
			case 'vvCode':
				$this->_getResultHtmlVvCode();
				break;

			default:
				return '';
		}
	}


	/**
	 * Gets the resulting HTML for reCaptcha and saves it to $resultHtml
	 */
	protected function _getResultHtmlRecaptcha()
	{
		include_once(DIR_FS_CATALOG . 'includes/recaptchalib.php');
		$html = recaptcha_get_html($this->publicKey, $this->error, true);
		$this->error = '';
		$contentView = MainFactory::create(self::_themeContentViewCheck('CaptchaContentView'));
		$contentView->setIsRecaptcha(true);
		$contentView->setCaptchaTheme($this->captchaTheme);
		$contentView->setPublicKey($this->publicKey);
		$contentView->setRecaptchaHtml($html);

		$this->setResultHtml($contentView->get_html());
	}

    /**
     * Gets the resulting HTML for reCaptcha and saves it to $resultHtml
     */
    protected function _getResultHtmlRecaptchaV2()
    {
        $contentView = MainFactory::create(self::_themeContentViewCheck('CaptchaContentView'));
        $contentView->setIsRecaptchaV2(true);
        $contentView->setPublicKey($this->publicKey);

        $this->setResultHtml($contentView->get_html());
    }


	/**
	 * Gets the resulting HTML for vvCode Captcha and saves it to $resultHtml
	 */
	protected function _getResultHtmlVvCode()
	{
		$contentView = MainFactory::create(self::_themeContentViewCheck('CaptchaContentView'));
		$contentView->setCaptchaName($this->vvCodeName);
		$contentView->setCaptchaUrl(xtc_href_link('request_port.php', 'rand=' . rand() . '&module=CreateVVCode', 'SSL', true, false));

		$this->setResultHtml($contentView->get_html());
	}

    /**
     * Checks whether the captcha response was a success from reCaptcha V2
     *
     * @return bool
     */
    protected function _validateCaptchaTypeRecaptchaV2(array $requestData)
    {
	    if(empty($requestData['g-recaptcha-response']))
	    {
		    return false;
	    }

	    // The data that needs to be in the query of the request
	    $data = [
		    'secret'   => $this->privateKey,
		    'response' => $requestData['g-recaptcha-response']
	    ];

	    $ch = curl_init();
	    curl_setopt($ch, CURLOPT_URL, self::$RECAPTCHA_V2_VERIFICATION_SERVER);
	    curl_setopt($ch, CURLOPT_POST, true);
	    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
	    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
	    $verify = curl_exec($ch);
	    curl_close($ch);

	    $response = json_decode($verify);
        
        if (json_last_error()) {
            return false;
        }

	    // Verify that the response was successful
	    if(!$response->success)
	    {
		    $this->error = $response->error->codes;

		    return false;
	    }

        return true;
    }

	/**
	 * @param array $requestData
	 *
	 * @return bool
	 */
	protected function _validateCaptchaTypeRecaptcha(array $requestData)
	{
		if(empty($requestData['recaptcha_response_field']) || empty($requestData['recaptcha_challenge_field']))
		{
			return false;
		}

		include_once(DIR_FS_CATALOG . 'includes/recaptchalib.php');
		$response = recaptcha_check_answer($this->privateKey, $_SERVER["REMOTE_ADDR"], $requestData['recaptcha_challenge_field'], $requestData['recaptcha_response_field']);

		if(!$response->is_valid)
		{
			$this->error = $response->error;
			return false;
		}

		return true;
	}


	/**
	 * @param array $requestData
	 * @param bool $isAjaxRequest
	 *
	 * @return bool
	 */
	protected function _validateCaptchaTypeVvcode(array $requestData, $isAjaxRequest)
	{
		if(empty($requestData[$this->vvCodeName]))
		{
			return false;
		}

		$vvCode = $this->vvCode;
		$this->vvCode = $_SESSION['vvcode'];

		if($isAjaxRequest)
		{
			$vvCode = $this->vvCode;
		}

		return strtoupper($requestData[$this->vvCodeName]) == $vvCode;
	}


	// ######### SETTER #########
	/**
	 * @param string $resultHtml
	 */
	public function setResultHtml($resultHtml)
	{
		$this->resultHtml = (string)$resultHtml;
	}

    /**
     * checks if the theme system is active
     * 
     * @deprecated 
     *
     * @return bool
     */
    protected static function _isThemeSystemActive()
    {
        return true;
    }

    /**
     * helper function to swap to theme content view if the theme system is active
     *
     * @param $contentView
     * @return mixed
     */
    protected static function _themeContentViewCheck($contentView)
    {
        return str_replace('ContentView', 'ThemeContentView', $contentView);;
    }
}
